// (c) Copyright 2013-2014 Stephen Gold <sgold@sonic.net>
// Distributed under the terms of the GNU General Public License

/*
 This file is part of the Bats Game.

 The Bats Game is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by the
 Free Software Foundation, either version 3 of the License, or (at your
 option) any later version.

 The Bats Game is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.

 You should have received a copy of the GNU General Public License
 along with the Bats Game.  If not, see <http://www.gnu.org/licenses/>.
 */
package bats.indicator;

import bats.Assets;
import bats.scene.NodeControl;
import bats.scene.SceneNode;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Cylinder;
import java.util.logging.Logger;

/**
 * A node control to manage the visible coordinate axes in a scene.
 * <p>
 * The axes are hidden when created.
 *
 * @author Stephen Gold <sgold@sonic.net>
 */
public class AxesIndicator
        extends NodeControl {
    // *************************************************************************
    // constants

    /**
     * radius of each axis cylinder (in meters)
     */
    final private static float radius = 0.05f;
    /**
     * color of the X-axis when visible.
     */
    final private static ColorRGBA xColor = ColorRGBA.Red;
    /**
     * color of the Z-axis when visible
     */
    final private static ColorRGBA zColor = ColorRGBA.Blue;
    /**
     * circumference of each cylinder, in vertices
     */
    final private static int circumferenceVertices = 4;
    /**
     * length of each cylinder, in vertices
     */
    final private static int lengthVertices = 2;
    /**
     * message logger for this class
     */
    final private static Logger logger =
            Logger.getLogger(AxesIndicator.class.getName());
    /**
     * name for the node in the scene graph
     */
    final private static String nodeName = "axes";
    // *************************************************************************
    // fields
    /**
     * distance from origin to nearest map edge (in meters, &gt;0): set in
     * constructor
     */
    final private float worldRadius;
    /**
     * geometry which represents the X-axis
     */
    private Geometry xAxis = null;
    /**
     * geometry which represents the Z-axis
     */
    private Geometry zAxis = null;
    // *************************************************************************
    // constructors

    /**
     * Instantiate hidden coordinate axes for the specified scene. After
     * instantiation, the first method invoked should be initialize().
     *
     * @param scene where the coordinate axes will reside (not null)
     * @param worldRadius distance from origin to nearest map edge (in meters,
     * &gt;0)
     */
    public AxesIndicator(SceneNode scene, float worldRadius) {
        super(scene);
        assert worldRadius > 0f : worldRadius;

        this.worldRadius = worldRadius;
        super.setEnabled(false);
    }
    // *************************************************************************
    // new methods exposed

    /**
     * Initialize this control.
     */
    public void initialize() {
        Node node = new Node(nodeName);
        node.addControl(this);
        /*
         * Attach the new node the scene graph as an indicator.
         */
        scene.getIndicators().attach(spatial);

        createSpatials();
    }
    // *************************************************************************
    // AbstractControl methods

    /**
     * Alter the visibility of these coordinate axes.
     *
     * @param newState if true, reveal the axes visible; if false, hide them
     */
    @Override
    public void setEnabled(boolean newState) {
        if (!enabled && newState) {
            attach(xAxis);
            attach(zAxis);
        } else if (enabled && !newState) {
            detach(xAxis);
            detach(zAxis);
        }
        super.setEnabled(newState);
    }
    // *************************************************************************
    // private methods

    /**
     * Create the spatials.
     */
    private void createSpatials() {
        /*
         * cylindrical mesh aligned with the Z-axis
         */
        float scaleFactor = scene.getScaleFactor();
        float length = 2f * worldRadius; // meters
        Cylinder mesh = new Cylinder(lengthVertices, circumferenceVertices,
                radius / scaleFactor, length / scaleFactor);

        xAxis = new Geometry("x axis", mesh);
        xAxis.rotate(0f, FastMath.HALF_PI, 0f);
        Material xAxisMaterial = Assets.createUnshadedMaterial(xColor);
        xAxis.setMaterial(xAxisMaterial);

        zAxis = new Geometry("z axis", mesh);
        Material zAxisMaterial = Assets.createUnshadedMaterial(zColor);
        zAxis.setMaterial(zAxisMaterial);
    }
}